//线段树-维护更多信息(差分与线段树结合)
//注意:求的是地雷的种类数, 不是覆盖次数!!!
//[l, r]地雷种类 = [1, r]起点数 - [1, l-1]的终点数
//[l, r]区间买上一颗地雷: l位置的起点数+1, r位置的终点数+1
//区间查询 + 单点修改

//写法一: 写两套函数分别对应起点修改和终点修改
// #include <iostream>
// using namespace std;
// #define lc p << 1
// #define rc p << 1 | 1
// typedef long long ll;
// const int N = 1e5 + 10;
// struct tree
// {
//     int l, r;
//     int s, f; //起点数, 终点数 
// }tr[N << 2];

// void pushup(int p)
// {
//     tr[p].s = tr[lc].s + tr[rc].s;
//     tr[p].f = tr[lc].f + tr[rc].f;
// }

// void build(int p, int l, int r)
// {
//     tr[p] = {l, r, 0, 0};
//     if(l == r) return;
//     int mid = (l + r) >> 1;
//     build(lc, l, mid); build(rc, mid + 1, r);
//     pushup(p);
// }

// //修改起点数
// void modifys(int p, int x)
// {
//     int l = tr[p].l, r = tr[p].r;
//     if(x == l && r == x)
//     {
//         tr[p].s++;
//         return;
//     }
//     int mid = (l + r) >> 1;
//     if(x <= mid) modifys(lc, x);
//     else modifys(rc, x); 
//     pushup(p);
// }

// //修改终点数
// void modifyf(int p, int x)
// {
//     int l = tr[p].l, r = tr[p].r;
//     if(x == l && r == x)
//     {
//         tr[p].f++;
//         return;
//     }
//     int mid = (l + r) >> 1;
//     if(x <= mid) modifyf(lc, x);
//     else modifyf(rc, x); 
//     pushup(p);
// }


// //查询[x, y]区间的起点数
// ll querys(int p, int x, int y)
// {
//     int l = tr[p].l, r = tr[p].r;
//     if(x <= l && r <= y) return tr[p].s;
//     int mid = (l + r) >> 1;
//     ll sum = 0;
//     if(x <= mid) sum += querys(lc, x, y);
//     if(y > mid) sum += querys(rc, x, y);
//     return sum;
// }

// //查询[x, y]区间的终点数
// ll queryf(int p, int x, int y)
// {
//     int l = tr[p].l, r = tr[p].r;
//     if(x <= l && r <= y) return tr[p].f;
//     int mid = (l + r) >> 1;
//     ll sum = 0;
//     if(x <= mid) sum += queryf(lc, x, y);
//     if(y > mid) sum += queryf(rc, x, y);
//     return sum;
// }

// int main()
// {
//     int n, m; cin >> n >> m;
//     build(1, 1, n);
//     while(m--)
//     {
//         int op, l, r; cin >> op >> l >> r;
//         if(op == 1)
//         {
//             modifys(1, l); modifyf(1, r);
//         }
//         else if(op == 2)
//         {
//             cout << querys(1, 1, r) - queryf(1, 1, l - 1) << endl;
//         }
//     }
//     return 0;
// }


//写法二: 多带一个参数, 只需一套函数
#include <iostream>
using namespace std;
#define lc p << 1
#define rc p << 1 | 1
typedef long long ll;
const int N = 1e5 + 10;
struct tree
{
    int l, r;
    int cnt[2]; //cnt[0]表示起点数, cnt[1]表示终点数
}tr[N << 2];

void pushup(int p)
{
    tr[p].cnt[0] = tr[lc].cnt[0] + tr[rc].cnt[0];
    tr[p].cnt[1] = tr[lc].cnt[1] + tr[rc].cnt[1];
}

void build(int p, int l, int r)
{
    tr[p] = {l, r, 0, 0};
    if(l == r) return;
    int mid = (l + r) >> 1;
    build(lc, l, mid); build(rc, mid + 1, r);
    pushup(p);
}

//修改起点/终点数
void modify(int p, int x, int k)
{
    int l = tr[p].l, r = tr[p].r;
    if(x == l && r == x)
    {
        tr[p].cnt[k]++;
        return;
    }
    int mid = (l + r) >> 1;
    if(x <= mid) modify(lc, x, k);
    else modify(rc, x, k); 
    pushup(p);
}

//查询[x, y]区间的起点/终点数
ll query(int p, int x, int y, int k)
{
    int l = tr[p].l, r = tr[p].r;
    if(x <= l && r <= y) return tr[p].cnt[k];
    int mid = (l + r) >> 1;
    ll sum = 0;
    if(x <= mid) sum += query(lc, x, y, k);
    if(y > mid)  sum += query(rc, x, y, k);
    return sum;
}

int main()
{
    int n, m; cin >> n >> m;
    build(1, 1, n);
    while(m--)
    {
        int op, l, r; cin >> op >> l >> r;
        if(op == 1)
        {
            modify(1, l, 0); modify(1, r, 1);
        }
        else if(op == 2)
        {
            cout << query(1, 1, r, 0) - query(1, 1, l - 1, 1) << endl;
        }
    }
    return 0;
}